package lelouet.sh3d.plugins.sweetDatacenter.model;

import gridlib.api.ConfigurationException;
import gridlib.api.IllegalLifeCycleException;
import gridlib.api.Machine;
import gridlib.api.MachineModel;
import gridlib.api.MachineLifeCycleController.MachineLifeCycleState;

import java.awt.EventQueue;
import java.rmi.RemoteException;

import com.eteks.sweethome3d.model.Home;
import com.eteks.sweethome3d.model.PieceOfFurniture;

public class GServer extends GElement {

	/**
	 * a {@link Runnable} element that will ask later the server to update its
	 * rack<br />
	 * It prevents too many updates on a server from using too much CPU.
	 */
	public class GServerRackUpdater implements Runnable {

		@Override
		public void run() {
			try {
				updateRackIfNeeded();
			} catch (Exception e) {
				System.err.println(e);
				e.printStackTrace(System.err);
			}
		}
	}

	/**
	 * @return the minimum integer number o U this requires to be placed in<br />
	 *         one U being 4.445 cm, a 4.4cm high server requires 1U and a
	 *         4.45cm high server requires 2
	 */
	public int getVSize() {
		try {
			return gridElement.getVSize();
		} catch (RemoteException e) {
			throw new RuntimeException("TODO: Handle this exception better", e);
		}
	}

	public void setVSize(int vsize) {
		try {
			gridElement.setVSize(vsize);
			GRack owner = this.owner;
			removeFromRack();
			setHeight(vsize * Utils.U_IN_CM);
			if (owner != null) {
				owner.addServer(this);
			}
		} catch (RemoteException e) {
			throw new RuntimeException("TODO: Handle this exception better", e);
		}
	}

	private static final long serialVersionUID = 1L;

	/**
	 * The {@link Home} that contains this server. Needed for searching another
	 * rack to put this in
	 */
	protected GridGraphicalBridge container;

	/** the rack that own the server, or null if this server is stand-alone */
	protected GRack owner = null;

	/** have there been a modification that request the server to be updated ? */
	protected boolean waitingForUpdate = false;

	protected GServerRackUpdater myUpdater = null;

	public GServer(GElementFactory bridge, final PieceOfFurniture model,
			final Machine base) {
		super(bridge, model);
		gridElement = base;
		bridge.selectNewRack(this);
	}

	protected Machine gridElement;

	protected Machine getGridElement() {
		return gridElement;
	}

	protected GRack getOwner() {
		return owner;
	}

	@Override
	public void move(final float dx, final float dy) {
		super.move(dx, dy);
		positionUpdated();
	}

	/** modify the position when the rack asks to */
	protected void moveWithinRack(final float dx, final float dy) {
		super.move(dx, dy);
	}

	protected void positionUpdated() {
		waitingForUpdate = true;
		if (myUpdater == null) {
			myUpdater = this.new GServerRackUpdater();
		}
		EventQueue.invokeLater(myUpdater);
	}

	/** remove this from the rack it is contained in */
	protected void removeFromRack() {
		if (owner != null) {
			owner.unsetServer(this);
			owner = null;
		}
		// System.out.println("  " + getName() + " removed from the rack");
	}

	@Override
	public void setElevation(final float elevation) {
		super.setElevation(elevation);
		positionUpdated();
	}

	/** modify the position when the rack asks to */
	protected void setElevationWithinRack(final float elevation) {
		super.setElevation(elevation);
	}

	protected void setOwner(final GRack owner) {
		this.owner = owner;
	}

	@Override
	public void setX(final float x) {
		super.setX(x);
		positionUpdated();
	}

	/** modify the position when the rack asks to */
	protected void setXWithinRack(final float x) {
		super.setX(x);
	}

	@Override
	public void setY(final float y) {
		super.setY(y);
		positionUpdated();
	}

	/** modify the position when the rack asks to */
	protected void setYWithinRack(final float y) {
		super.setY(y);
	}

	@Override
	public String getName() {
		try {
			return gridElement.getName();
		} catch (RemoteException e) {
			e.printStackTrace(System.err);
			return "";
		}
	}

	@Override
	public void setName(String name) {
		try {
			boolean modifiedState = gridElement.getState() == MachineLifeCycleState.ON;
			if (modifiedState) {
				gridElement.setState(MachineLifeCycleState.OFF);
			}
			gridElement.setName(name);
			if (modifiedState) {
				gridElement.setState(MachineLifeCycleState.ON);
			}
		} catch (Exception e) {
			e.printStackTrace(System.err);
		}
	}

	/**
	 * try to update the rack the server is contained in, if it still has to<br />
	 * No synchronization is done, it aims at best-effort
	 */
	protected void updateRackIfNeeded() {
		if (!waitingForUpdate) {
			return;
		} else {
			waitingForUpdate = false;
		}
		// System.out.println(" updating " + getName());
		removeFromRack();
		bridge.selectNewRack(this);
	}

	@Override
	public String toString() {
		return getName();
	}

	public int getCPUCapacity() {
		try {
			return gridElement.getCpuCapacity();
		} catch (RemoteException e) {
			e.printStackTrace();
			return 0;
		}
	}

	public void setCPUCapacity(double cpucapacity) {
		try {
			gridElement.setCpuCapacity(cpucapacity);
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (IllegalLifeCycleException e) {
			e.printStackTrace();
		}
	}

	public int getCPUNumber() {
		try {
			return gridElement.getCpuNumber();
		} catch (RemoteException e) {
			e.printStackTrace();
			return 1;
		}
	}

	public void setCPUNumber(double number) {
		try {
			gridElement.setCpuNumber(number);
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (IllegalLifeCycleException e) {
			e.printStackTrace();
		}
	}

	public int getMemoryCapacity() {
		try {
			return gridElement.getMemory();
		} catch (RemoteException e) {
			e.printStackTrace();
			return 1;
		}
	}

	public void setMemoryCapacity(double number) {
		try {
			gridElement.setMemory(number);
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (IllegalLifeCycleException e) {
			e.printStackTrace();
		}
	}

	// getVSize is already defined

	public String getOSFamily() {
		try {
			return gridElement.getOsfamily();
		} catch (RemoteException e) {
			e.printStackTrace();
			return "exceptionnal familly";
		}
	}

	public void setOSFamily(String family) {
		try {
			gridElement.setOsFamily(family);
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (IllegalLifeCycleException e) {
			e.printStackTrace();
		}
	}

	public String getOSDistrib() {
		try {
			return gridElement.getOsDistrib();
		} catch (RemoteException e) {
			e.printStackTrace();
			return "exceptionnal distrib";
		}
	}

	public void setOSDistrib(String distrib) {
		try {
			gridElement.setOsDistrib(distrib);
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (IllegalLifeCycleException e) {
			e.printStackTrace();
		}
	}

	// getName and setName already present

	public String getIP() {
		try {
			return gridElement.getIp();
		} catch (RemoteException e) {
			e.printStackTrace();
			return "exceptionnal distrib";
		}
	}

	public void setIP(String ip) {
		try {
			gridElement.setIp(ip);
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (ConfigurationException e) {
			e.printStackTrace();
		}
	}

	public String getMac() {
		try {
			return gridElement.getMac();
		} catch (RemoteException e) {
			e.printStackTrace();
			return "exceptionnal distrib";
		}
	}

	public void setMac(String mac) {
		try {
			gridElement.setMac(mac);
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (IllegalLifeCycleException e) {
			e.printStackTrace();
		} catch (ConfigurationException e) {
			e.printStackTrace();
		}
	}

	public MachineModel getGridModel() {
		try {
			return gridElement.getModel();
		} catch (RemoteException e) {
			e.printStackTrace();
			return null;
		}
	}

	public void setGridModel(MachineModel model) {
		try {
			gridElement.setModel(model);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void removeGridReferences() {
		try {
			if (gridElement.getLevel() != null) {
				gridElement.getLevel().removeMachine();
			}
			bridge.geFactory.removeFromGlobals(getGridElement());
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (ConfigurationException e) {
			e.printStackTrace();
		}

	}

	@Override
	public void graphicalDelete() {
		removeFromRack();
		removeGridReferences();
		bridge.gservers.remove(this);
	}
}
